<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-hemisu.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-hemisu.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-hemisu.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="javascript,">










<meta name="description" content="js的异步编程是我们无法避免的话题之一：我们会在平时的编码过程中用到它，也会在面试中遇到它（大雾） 今天就对js的异步发展进行简单的探索，并且顺着流程了解并实现一个符合规范的promise来帮助更好地理解js中异步编程的处理逻辑： 本文第一章节将花费6～8min阅读时间；第二章如果不着手实现将花费10～15min理解时间；参与实现将花费1h :D第三章将花费4～6min阅读时间； 行文仓促，如有错">
<meta name="keywords" content="javascript">
<meta property="og:type" content="article">
<meta property="og:title" content="js异步初探 - promise">
<meta property="og:url" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/index.html">
<meta property="og:site_name" content="何米酥`s Blog">
<meta property="og:description" content="js的异步编程是我们无法避免的话题之一：我们会在平时的编码过程中用到它，也会在面试中遇到它（大雾） 今天就对js的异步发展进行简单的探索，并且顺着流程了解并实现一个符合规范的promise来帮助更好地理解js中异步编程的处理逻辑： 本文第一章节将花费6～8min阅读时间；第二章如果不着手实现将花费10～15min理解时间；参与实现将花费1h :D第三章将花费4～6min阅读时间； 行文仓促，如有错">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/668BA494D5A6AA497ABE50455518D706.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/9C12BB0E7427701125584DA8535B8CA6.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/AA7F49C6D13DA4DB21FD12805526E9F2.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/E17178C5955C6292BE26C8EAF3707E46.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/966DAB00C8C1446882F06EB101F4AB95.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/B7BC3B9378E53746C3D277E8370B16E8.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/8DA0EB206C2F0C87BAFEB9FF2A3FDBA2.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/A341EC4D95771B63DEDF1D075FB22E0B.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/EDA6100E00940EDC16E34D1111856C0C.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/CE51A39E3AAF4592AF69A73B34976D3F.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/171C910237C1CC49F9965242CEE5F6C4.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/2CCF675B33BF2CC92C0C1AD5855E6CCD.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/92BE4E052B27172D4C27830D72CF4954.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/E4820D8BB88684DEFE25B089A237B26E.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/D8F18AD68D4E0284446C78F2F6694AF8.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/E07F6E6B1F1DA639D5FBA7FDE9FE3BBC.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/493BDF847706C3972B6F8501DBFACE82.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/C825E85B437029689F170347582711ED.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/F876C08CDAE85ECE52E29B51522F6354.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/BEA4BB425D88316B7B34D7D775BAB835.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/3F024598C245C50FD8C920C0F8ABC486.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/2F15C716CE0A3405CFBBFDA2F1EF7E14.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/ABFB1DBD9BB5DBAF203D0A18339370C5.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/FCCEDE45412B545C613A24AD01F12664.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/3C8D9EF3C70C59B930AD8423AA2D0EBA.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/8FB32EF8C0DA6B0B82E68F63F1B0844B.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/223EDF02DF00FB49EC748E72BF34DDA6.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/C7C592FBB53E9C43F6FE6032772807D9.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/03DA27A461C8807C32A589D19C1193E9.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/6EC23FAAFDF54CAFD9AA7D9A20620637.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/AAB4878614D10C4DFE9A05B531E05484.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/CE07C5A06BE97BA8F62F00604CA42898.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/182425CE19C05E16A5AD2D5631AE2FEA.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/4C2A96F3E8BA48D1C40BE67F84ED4D3E.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/A4CA555A1D97807117682BB7B8B2233A.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/A8DF6F5D79C0E2EB9B9C138C846E4B2A.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/305E536E9AE1C3D8BC74C284C27D441D.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/25393614A3D21E15B2D75EB6CC667F46.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/1F5F81C12726EC5BCDA78E15E54A7E35.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/FE03CF8560B940DA8051BF0D8F8F2AE1.jpg">
<meta property="og:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/786E3BB5C3937897950BB2EB47F5C120.jpg">
<meta property="og:updated_time" content="2020-09-27T05:22:33.716Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="js异步初探 - promise">
<meta name="twitter:description" content="js的异步编程是我们无法避免的话题之一：我们会在平时的编码过程中用到它，也会在面试中遇到它（大雾） 今天就对js的异步发展进行简单的探索，并且顺着流程了解并实现一个符合规范的promise来帮助更好地理解js中异步编程的处理逻辑： 本文第一章节将花费6～8min阅读时间；第二章如果不着手实现将花费10～15min理解时间；参与实现将花费1h :D第三章将花费4～6min阅读时间； 行文仓促，如有错">
<meta name="twitter:image" content="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/668BA494D5A6AA497ABE50455518D706.jpg">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":true,"scrollpercent":true,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/">





  <title>js异步初探 - promise | 何米酥`s Blog</title>
  








  <!-- Hotjar Tracking Code for www.hemisu.com -->
  <script>
      (function(h,o,t,j,a,r){
          h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
          h._hjSettings={hjid:1933736,hjsv:6};
          a=o.getElementsByTagName('head')[0];
          r=o.createElement('script');r.async=1;
          r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
          a.appendChild(r);
      })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
  </script>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">何米酥`s Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">EFE</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br>
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://www.hemisu.com/2019/11/04/javascript-concurrency-promise/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="何米酥">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="何米酥`s Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">js异步初探 - promise</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-11-04T20:36:11+00:00">
                2019-11-04
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/学习/" itemprop="url" rel="index">
                    <span itemprop="name">学习</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>js的异步编程是我们无法避免的话题之一：我们会在平时的编码过程中用到它，也会在面试中遇到它（大雾）</p>
<p>今天就对js的异步发展进行简单的探索，并且顺着流程了解并实现一个符合规范的promise来帮助更好地理解js中异步编程的处理逻辑：</p>
<p>本文第一章节将花费6～8min阅读时间；<br>第二章如果不着手实现将花费10～15min理解时间；参与实现将花费1h :D<br>第三章将花费4～6min阅读时间；</p>
<p>行文仓促，如有错误还请指正。</p>
<ul>
<li><a href="#1-%e5%89%8d%e8%a8%80">1. 前言</a><ul>
<li><a href="#11-%e4%b8%ba%e4%bd%95%e4%bc%9a%e6%9c%89%e5%bc%82%e6%ad%a5">1.1 为何会有异步</a></li>
<li><a href="#12-%e5%bc%82%e6%ad%a5%e7%9a%84%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86">1.2 异步的实现原理</a><ul>
<li><a href="#121-%e5%b8%b8%e8%a7%81%e7%9a%84%e5%bc%82%e6%ad%a5%e6%93%8d%e4%bd%9c">1.2.1 常见的异步操作：</a></li>
<li><a href="#122-%e4%ba%8b%e4%bb%b6%e7%bb%91%e5%ae%9a%e7%ae%97%e4%b8%8d%e7%ae%97%e5%bc%82%e6%ad%a5%e6%93%8d%e4%bd%9c">1.2.2 事件绑定算不算异步操作？</a></li>
</ul>
</li>
<li><a href="#13-%e5%bc%82%e6%ad%a5%e7%9a%84%e6%bc%94%e8%bf%9b%e6%96%b9%e6%a1%88">1.3 异步的演进方案</a><ul>
<li><a href="#131-deferred%e5%92%8cpromise%e6%9c%89%e4%bb%80%e4%b9%88%e5%8c%ba%e5%88%ab">1.3.1 <code>$.deferred</code>和<code>$.promise</code>有什么区别？</a></li>
<li><a href="#132-callback%e7%9a%84%e5%bc%95%e5%85%a5">1.3.2 callback的引入</a></li>
<li><a href="#133-promise%e4%b9%9f%e6%98%afcallback">1.3.3 promise也是callback？</a></li>
<li><a href="#134-generator%e4%b9%9f%e6%98%afcallback">1.3.4 generator也是callback？</a></li>
<li><a href="#135-%e6%9c%80%e5%90%8e%e6%bc%94%e8%bf%9b%e5%88%b0asyncawait">1.3.5 最后演进到async/await</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#2-promise">2 Promise</a><ul>
<li><a href="#21-%e6%a6%82%e8%a7%88">2.1 概览</a><ul>
<li><a href="#211-%e5%9b%be21%e4%b8%ad%e4%bf%a9%e8%80%85%e7%9a%84promise%e6%9c%89%e5%95%a5%e5%8c%ba%e5%88%ab">2.1.1 图2.1中俩者的promise有啥区别？</a></li>
</ul>
</li>
<li><a href="#22-%e5%ae%9e%e7%8e%b0%e4%b8%80%e4%b8%aa-promisea-%e8%a7%84%e8%8c%83">2.2 实现一个 Promise/A+ 规范</a><ul>
<li><a href="#221-%e5%89%8d%e6%9c%9f%e5%b7%a5%e4%bd%9c">2.2.1 前期工作</a></li>
<li><a href="#222-%e5%bc%80%e5%a7%8b%e5%ae%9e%e7%8e%b0">2.2.2 开始实现</a></li>
<li><a href="#223-%e6%9c%af%e8%af%ad---1terminology">2.2.3 术语 - 1.terminology</a></li>
<li><a href="#224-promise%e7%9a%84%e7%8a%b6%e6%80%81---21-promise-states">2.2.4 promise的状态 - 2.1 Promise States</a></li>
<li><a href="#225-then-%e6%96%b9%e6%b3%95---22-the-then-method">2.2.5 then 方法 - 2.2 The then Method</a></li>
<li><a href="#226-then-%e6%96%b9%e6%b3%95---23-the-promise-resolution-procedure">2.2.6 then 方法 - 2.3 The Promise Resolution Procedure</a></li>
</ul>
</li>
<li><a href="#23-%e5%a2%9e%e5%bc%ba%e4%b8%baes6%e7%9a%84promise">2.3 增强为ES6的promise</a><ul>
<li><a href="#231-promiseprototypecatch">2.3.1 Promise.prototype.catch</a></li>
<li><a href="#232-promiseall">2.3.2 Promise#all</a></li>
<li><a href="#232-promiserace">2.3.2 Promise#race</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#3-%e6%80%bb%e7%bb%93">3 总结</a><ul>
<li><a href="#31%e5%ae%9e%e7%8e%b0promise%e4%b9%8b%e5%90%8e%e7%9a%84%e6%80%9d%e8%80%83">3.1实现promise之后的思考</a><ul>
<li><a href="#311-%e5%ae%83%e6%98%af%e4%b8%80%e4%b8%aacallback-management">3.1.1 它是一个callback management</a></li>
<li><a href="#312-%e8%83%bd%e6%89%8b%e5%86%99promies%e5%ae%9e%e7%8e%b0%e5%b0%b1%e4%b8%80%e5%ae%9a%e7%b2%be%e9%80%9apromies">3.1.2 能手写promies实现，就一定精通promies？</a></li>
<li><a href="#313-promise-%e6%98%af%e6%af%94-callback-%e6%9b%b4%e5%85%88%e8%bf%9b%e7%9a%84%e5%bc%82%e6%ad%a5%e6%96%b9%e6%a1%88">3.1.3 Promise 是比 callback 更先进的异步方案？</a></li>
</ul>
</li>
<li><a href="#32-%e5%8f%8d%e6%a8%a1%e5%bc%8f">3.2 反模式</a><ul>
<li><a href="#321-anti-patterns-1">3.2.1 Anti-patterns 1</a></li>
<li><a href="#322-anti-patterns-2">3.2.2 Anti-patterns 2</a></li>
<li><a href="#323-anti-patterns-3">3.2.3 Anti-patterns 3</a></li>
</ul>
</li>
<li><a href="#33-%e9%99%84%e5%bd%95">3.3 附录</a></li>
<li><a href="#%e7%ad%94%e6%a1%881-%e4%ba%8b%e4%bb%b6%e7%bb%91%e5%ae%9a%e7%ae%97%e4%b8%8d%e7%ae%97%e5%bc%82%e6%ad%a5%e6%93%8d%e4%bd%9c">答案1 事件绑定算不算异步操作</a></li>
</ul>
</li>
</ul>
<a id="more"></a>
<h1 id="1-前言"><a href="#1-前言" class="headerlink" title="1. 前言"></a>1. 前言</h1><h2 id="1-1-为何会有异步"><a href="#1-1-为何会有异步" class="headerlink" title="1.1 为何会有异步"></a>1.1 为何会有异步</h2><p>  首先我们从为何会有异步入手，主要是有一下子两点原因： </p>
<ol>
<li>JS被涉及成单线程的语言，它自身只有一个单一的Call Stack，这就决定了它在某一时刻只能实现一件事情，因此它是同步的；</li>
<li>目前我们所使用的异步实现都需要平台的支撑（v8或者其他引擎）其次，浏览器是异步的，且为了用户体验不可避免；比如我们打开哔哩哔哩这个网站，就可以发现在同一时间，浏览器的多个模块同时工作，包括：网络请求、渲染界面、DOM事件、定时器等； </li>
</ol>
<p><img src="668BA494D5A6AA497ABE50455518D706.jpg" alt="IMAGE"><br>图1.1 V8引擎</p>
<p><img src="9C12BB0E7427701125584DA8535B8CA6.jpg" alt="IMAGE"><br>图1.2 浏览器打开b站所做的工作</p>
<h2 id="1-2-异步的实现原理"><a href="#1-2-异步的实现原理" class="headerlink" title="1.2 异步的实现原理"></a>1.2 异步的实现原理</h2><p>这里我们可以看一段网络请求代码，如图1.3：接收两个参数，其中success是一个函数，这个函数传递进去不会立即执行，而是等待请求之后再运行这行函数。对于这种传递过去不执行，等待结果之后才执行的函数，我们称他为<strong>回调函数</strong>（callback）。</p>
<p><img src="AA7F49C6D13DA4DB21FD12805526E9F2.jpg" alt="IMAGE"><br>图1.3 网络请求代码</p>
<p>我们再来看一段nodejs中的代码，区别于前者的是，如图1.4所示的是一段文件I/O的处理。</p>
<p><img src="E17178C5955C6292BE26C8EAF3707E46.jpg" alt="IMAGE"><br>图1.4 nodejs中文件I/O的处理</p>
<p>由此看来，实现异步的最核心原理在于：<strong>将callback作为参数传递给异步执行函数，当有结果之后再触发callback执行</strong></p>
<p>我们可以收集一波常用的异步操作，大致分为以下三种：网络请求、IO操作、定时任务；</p>
<h3 id="1-2-1-常见的异步操作："><a href="#1-2-1-常见的异步操作：" class="headerlink" title="1.2.1 常见的异步操作："></a>1.2.1 常见的异步操作：</h3><ul>
<li>网络请求： ajax、http.get</li>
<li>IO操作：readFile、readdir</li>
<li>定时函数： setTimeout、setInterval</li>
</ul>
<p>那么，在这里提出第一个问题：</p>
<h3 id="1-2-2-事件绑定算不算异步操作？"><a href="#1-2-2-事件绑定算不算异步操作？" class="headerlink" title="1.2.2 事件绑定算不算异步操作？"></a>1.2.2 事件绑定算不算异步操作？</h3><p><img src="966DAB00C8C1446882F06EB101F4AB95.jpg" alt="IMAGE"><br>图1.5 事件绑定</p>
<p>在公布答案之前，我们对比一下以下两者之间的区别：<br><img src="B7BC3B9378E53746C3D277E8370B16E8.jpg" alt="IMAGE"><br>图1.6 事件绑定对比</p>
<p>他们写法相同、执行方式也相同，都会将callback放入call-stack中通过eventloop调用；</p>
<p>那么他们的不同之处在于：调用源不同，前者是用户手动触发、后者是系统读取完文件后调用；此外前者有着比较明显的“订阅-发布”的设计模式痕迹，后者则没有；</p>
<details><br>  <summary style="font-size: 16px; font-weight: 700"><br>    请思考后展开答案<br>  </summary><br>  <span id="17:31:42"><a href="#17:31:42-1">答案1 事件绑定算不算异步操作</a></span><br>  是也不是，关键要看触发源来自于哪。<br></details>

<h2 id="1-3-异步的演进方案"><a href="#1-3-异步的演进方案" class="headerlink" title="1.3 异步的演进方案"></a>1.3 异步的演进方案</h2><p>关于异步方案的演进，我们可以从图1.7梳理出这样一条链路</p>
<p><img src="8DA0EB206C2F0C87BAFEB9FF2A3FDBA2.jpg" alt="IMAGE"><br>图1.7 异步方案的演进</p>
<blockquote>
<p><img src="A341EC4D95771B63DEDF1D075FB22E0B.jpg" alt="IMAGE"></p>
</blockquote>
<p>callback到promise阶段发生在jquery时代，在jQuery 1.5版本 以前还有deferred的出现，之后在此基础上出现了promise；</p>
<p>那么在这里再提出一个问题：</p>
<h3 id="1-3-1-deferred和-promise有什么区别？"><a href="#1-3-1-deferred和-promise有什么区别？" class="headerlink" title="1.3.1 $.deferred和$.promise有什么区别？"></a>1.3.1 <code>$.deferred</code>和<code>$.promise</code>有什么区别？</h3><p>首先说明，这里的deferred和promise都是jQuery上的对象，不是我们所熟悉的promise。<br>在回答这个问题之前，先介绍一下deferrd：</p>
<p><img src="EDA6100E00940EDC16E34D1111856C0C.jpg" alt="IMAGE"></p>
<p>deferred有以上5种方法，我们将它区分为两类，前者属于用户掌握控制权，控制deferred对象的状态，后者则将这个权利交给它自身。</p>
<details><br>  <summary style="font-size: 16px; font-weight: 700"><br>    请思考后展开答案<br>  </summary><br>  他们的区别是：promise对象相比于deferred对象，缺少了.resolve和.reject这两个函数属性（没错，与如今的promie有略微区别），你无法在上述划分的第二类方法以外去决议(resolve or reject)一个promise的状态（这个与如今类似）；而deferred可以在外边直接决议；<br></details> 

<p>Promise在这里更多地被作为一个异步流程控制工具，而非一个订阅发布的事件模块，在此屏蔽了外部状态的改变。（当然，非主流之外的标准大家有兴趣可以自行搜寻或者看参考链接）</p>
<p>按照主流的说法，callback被认为是一个最差的方案，会出现类似于回调地狱这类问题等；而async/await则被认为是所谓异步的终极解决方案。<br><img src="CE51A39E3AAF4592AF69A73B34976D3F.jpg" alt="IMAGE"><br>图1.8 回调地狱</p>
<h3 id="1-3-2-callback的引入"><a href="#1-3-2-callback的引入" class="headerlink" title="1.3.2 callback的引入"></a>1.3.2 callback的引入</h3><p><img src="171C910237C1CC49F9965242CEE5F6C4.jpg" alt="IMAGE"><br>图1.9 callback</p>
<p>从流行的趋势来看，这种说法不无道理，我们确实从callback style一步步走到了今天大量使用 async/await 的阶段。那么，是否真是如此？</p>
<h3 id="1-3-3-promise也是callback？"><a href="#1-3-3-promise也是callback？" class="headerlink" title="1.3.3 promise也是callback？"></a>1.3.3 promise也是callback？</h3><p>稍后我们要介绍的主角Promise，在它的Promise/A+规范中有这么一句表述：<br><img src="2CCF675B33BF2CC92C0C1AD5855E6CCD.jpg" alt="IMAGE"></p>
<blockquote>
<p>Promise 表示一个异步操作的最终结果，与之进行交互的方式主要是<code>then</code>方法，该方法注册了两个<strong>回调函数</strong>，用于接收 promise 的终值或本 promise 不能执行的原因。</p>
</blockquote>
<p>Callback是一个很宽泛的概念，我们可以把promise当作是一个能处理0-1个异步结果的状态管理器：因此，我们可以把callback -&gt; promise的演进看作是一种style到另一种style的过程。<br><img src="92BE4E052B27172D4C27830D72CF4954.jpg" alt="IMAGE"><br>图1.10 promise</p>
<h3 id="1-3-4-generator也是callback？"><a href="#1-3-4-generator也是callback？" class="headerlink" title="1.3.4 generator也是callback？"></a>1.3.4 generator也是callback？</h3><p>随着进一步演进，在社区中出现了co库将generator与promise结合实现同步写法编写异步代码的解决方案：</p>
<p><img src="E4820D8BB88684DEFE25B089A237B26E.jpg" alt="IMAGE"><br>图1.11 co-generator</p>
<p>你们猜我是不是想说这也是一种callback style？</p>
<p>没错。。<br>我们可以把generator函数理解为：<br>它是一个把执行权一步一步交出去的函数：交出去后怎么回来？这就得调用callback函数。<br>当然事实没有这么简单，在这个过程中还需要做许多工作，需要恢复上下文执行栈环境等等。</p>
<p>为了印证这个说法，我们来看一个例子：如何将generator视为多个callback的函数的整合。</p>
<p><img src="D8F18AD68D4E0284446C78F2F6694AF8.jpg" alt="IMAGE"><br>图1.12 genetator的一个例子</p>
<p>如图1.12所示的代码，我们去掉*和yeild关键字，用朴素函数简化后变成图1.13这个样子。</p>
<p><img src="E07F6E6B1F1DA639D5FBA7FDE9FE3BBC.jpg" alt="IMAGE"><br>图1.13 通过朴素函数转化一个generator</p>
<p>就这样，我们构造了一个嵌套了 4 层的 callback，然后在 next 函数的 4 次调用中，分别解开一层层的回调，最后清空了 callback 的层次。实现了与前面的 generator function 相似的行为。</p>
<p>当然，在babel中并不是通过上述方式来实现的，而是利用一个合成函数，通过switch case将它分割成多块，每次只执行其中一个case：<br><img src="493BDF847706C3972B6F8501DBFACE82.jpg" alt="IMAGE"><br>图1.14 babel转化generator的解决方案</p>
<h3 id="1-3-5-最后演进到async-await"><a href="#1-3-5-最后演进到async-await" class="headerlink" title="1.3.5 最后演进到async/await"></a>1.3.5 最后演进到async/await</h3><p>至于generator到async/await的演进，我们可以视为它是promise + generator这个方案的语义化和标准化。</p>
<p>至此，我们可以将JavaScript里的异步方案演进表述为以下形式<sup><a href="https://mp.weixin.qq.com/s/qdJ0Xd8zTgtetFdlJL3P1g" title="100行实现一个Promise/A+" target="_blank" rel="noopener">1</a></sup>：<br>Raw Callback Style -&gt; Promise Callback Style -&gt; Generator Callback Style -&gt; Async/Await Callback</p>
<p><img src="C825E85B437029689F170347582711ED.jpg" alt="IMAGE"><br>图1.15 JavaScript异步方案演进的表述</p>
<p>新的表述跟旧的表述，总体上是一致的。只是描述口径从 4 个不同事物的演进，变成了同种事物的不同形态的演进。</p>
<p>语义化和标准化，不意味着能力的增强，它也有可能导致能力的减弱。</p>
<ol>
<li>比如generator和async-await对比，generator 既能支持同步行为，也能支持异步行为；而async只支持异步行为。</li>
<li>再比如并行处理，我们需要在async函数中使用promise.all才能达到目的</li>
<li>最后callback虽然被人诟病，但是如果有如图1.16的需求，那又该如何实现？</li>
</ol>
<p><img src="F876C08CDAE85ECE52E29B51522F6354.jpg" alt="IMAGE"><br>图1.16 控制并发数的异步请求控制</p>
<p>所以说，如果我们愿意手动管理callback，理论上最原始的callback的表达能力是最强的；<br>这也很符合这个世界的规律：<strong>等价交换</strong>；</p>
<p><img src="BEA4BB425D88316B7B34D7D775BAB835.jpg" alt="IMAGE"><br>图1.17 有失有得的异步方案演进<br>从上往下，我们编写代码的方式越来越方便，更易维护；为此付出的代价就是能力削弱，编译器需要识别更多关键字；</p>
<p>初步介绍完js的异步发展过程，我们就开始介绍今日的主角promise：</p>
<h1 id="2-Promise"><a href="#2-Promise" class="headerlink" title="2 Promise"></a>2 Promise</h1><h2 id="2-1-概览"><a href="#2-1-概览" class="headerlink" title="2.1 概览"></a>2.1 概览</h2><p><img src="3F024598C245C50FD8C920C0F8ABC486.jpg" alt="IMAGE"><br>图2.1 不同的Promise</p>
<p>通过之前的描述，我们明白promise的出现那一定是迟早的事，并且promise还有很多兄弟姐妹；<br>有兴趣的同学可以去看看《聊一聊promise的前世今生》这篇文章<sup><a href="https://www.cnblogs.com/tarol/p/9042407.html" title="聊一聊promise的前世今生" target="_blank" rel="noopener">2</a></sup>。</p>
<p>如今我们使用的promise更多是在Promies/A+ 基础上增强的Promise ES6标准。</p>
<p>今天的另一个问题就是:</p>
<h3 id="2-1-1-图2-1中俩者的promise有啥区别？"><a href="#2-1-1-图2-1中俩者的promise有啥区别？" class="headerlink" title="2.1.1 图2.1中俩者的promise有啥区别？"></a>2.1.1 图2.1中俩者的promise有啥区别？</h3><details><br>  <summary style="font-size: 16px; font-weight: 700"><br>    请思考后展开答案<br>  </summary><br>  他们的区别是：ES6中的Promise增加了 <code>.catch</code> 方法，增加了 <code>Promsie.resolve</code>、<code>Promise.reject</code>、 <code>Promise.all</code>、<code>Promise.race</code>等静态方法<br></details> 

<p>作为现代JavaScript中的一部分，理解和掌握Promise对前端开发者异常重要。它几乎成为面试中必问的问题之一，甚至还有一些付费教程教学如何实现一个Promise。</p>
<p>依我之见，互联网上资料丰富，但是也是鱼龙混杂：我们可以通过搜索引擎搜到与关键字相关的NNNN条信息，但是不一定能找到自己想要的信息。可能是我们搜索的关键字、可能是信息太多无法筛选、可能是因为坦克原因不可见、也可能是作者表述与读者理解力的不对等。</p>
<p>我将尽量以一种能看懂的方式实现Promise/A+规范。</p>
<p>不过，正如我所参考的这个实现作者所说的<sup><a href="https://mp.weixin.qq.com/s/qdJ0Xd8zTgtetFdlJL3P1g" title="100行实现一个Promise/A+" target="_blank" rel="noopener">1</a></sup>：</p>
<blockquote>
<p>不过，如果一个初学者，想通过实现 Promises/A+ 去学习 Promises。或者认为实现了 Promises/A+规范后，对 Promises 的理解水平能得到质的提升。最后可能会失望。</p>
</blockquote>
<blockquote>
<p>实际上，Promises/A+ 规范，内容简短，实现难度低。其中充斥着细节行为的描述，缺乏设计目的和背景的部分，完全没有介绍使用场景。并不是一个入门 Promises 的好材料。</p>
</blockquote>
<blockquote>
<p>即便成功实现 Promises/A+ 规范，也不一定比没实现过的开发者，更善于使用 Promises 特性。</p>
</blockquote>
<p>如果想更好地使用Promise，我会推荐你去看promise迷你书<sup><a href="http://liubin.org/promises-book/" title="promise迷你书" target="_blank" rel="noopener">3</a></sup></p>
<h2 id="2-2-实现一个-Promise-A-规范"><a href="#2-2-实现一个-Promise-A-规范" class="headerlink" title="2.2 实现一个 Promise/A+ 规范"></a>2.2 实现一个 Promise/A+ 规范</h2><p>我们会使用Promise/A+作者提供的测试套件来测试我们实现的Promise是否符合规范。</p>
<p>现在跟着我一起左手🐲右手🌈</p>
<h3 id="2-2-1-前期工作"><a href="#2-2-1-前期工作" class="headerlink" title="2.2.1 前期工作"></a>2.2.1 前期工作</h3><p>英文规范： <a href="https://promisesaplus.com/" target="_blank" rel="noopener">https://promisesaplus.com/</a><br>中文规范： <a href="https://www.ituring.com.cn/article/66566" target="_blank" rel="noopener">https://www.ituring.com.cn/article/66566</a><br>测试套件： promises-aplus-tests</p>
<p>git repo: <a href="https://github.com/hemisu/promise-aplus-impl" target="_blank" rel="noopener">https://github.com/hemisu/promise-aplus-impl</a></p>
<p>左侧打开promise规范网页，右侧编写代码撒。<br>创建一个目录，再命令行执行：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/hemisu/promise-aplus-impl</span><br><span class="line"><span class="built_in">cd</span> promise-aplus-impl &amp;&amp; npm install</span><br><span class="line">npm run <span class="built_in">test</span></span><br></pre></td></tr></table></figure></p>
<p>这样你就可以看到自带的promise的测试结果啦：D</p>
<h3 id="2-2-2-开始实现"><a href="#2-2-2-开始实现" class="headerlink" title="2.2.2 开始实现"></a>2.2.2 开始实现</h3><p>首先取消掉<code>src/index.js</code>中第一行注释，引入我们自己编写的promise</p>
<p>在<code>src/promies.js</code>中已经准备好了后面需要使用的一部分类型检测代码。<br>接着我们就开始对照着Promise/A+规范开始编码（推荐使用英文原版，有标注规范条目，不理解的地方再对照图灵中文翻译）。</p>
<h3 id="2-2-3-术语-1-terminology"><a href="#2-2-3-术语-1-terminology" class="headerlink" title="2.2.3 术语 - 1.terminology"></a>2.2.3 术语 - 1.terminology</h3><p><img src="2F15C716CE0A3405CFBBFDA2F1EF7E14.jpg" alt="IMAGE"><br>图2.1 Terminology</p>
<p>规范的第一部分描述了几个术语，它们分别如下：</p>
<ul>
<li><code>promise</code> 是一个包含 <code>then</code> 方法的对象或者函数</li>
<li><code>thenable</code> 是一个包含 <code>then</code> 方法的对象或者函数</li>
<li><code>value</code> 是一个合法JS值，可以是一个 <code>undefined</code> <code>thenable</code> <code>promose</code></li>
</ul>
<p>这里解释一下，value是一个promise内部保持的值，它决议（resolve）成功时我们可以称它为result，而它失败(reject)时我们可以称它为reason。</p>
<p>对应这一节的规范，我们可以编写对应的代码（部分已内置）：<br>这里的 <code>thenable</code> 涉及到鸭子类型(Duck Typing)。鸭子类型可解释为,如果一只动物,走起来像鸭子或者叫起来像鸭子,就可以把它当作鸭子。同理，一个对象或者函数如果有 <code>then</code> 方法，我们就可以称它为 <code>thenable</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// utils</span></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">const</span> isThenable = <span class="function"><span class="params">obj</span> =&gt;</span> (isObject(obj) || isFunction(obj)) &amp;&amp; <span class="string">'then'</span> <span class="keyword">in</span> obj</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// begin here</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Promise</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.result = <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Promise</span>.prototype.then = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// </span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这一部分没有多少代码需要添加，我们只需要在构造函数中加入一行<code>value</code>的内置值就行；另外在原型上挂载一个 <code>then</code> 方法</p>
<h3 id="2-2-4-promise的状态-2-1-Promise-States"><a href="#2-2-4-promise的状态-2-1-Promise-States" class="headerlink" title="2.2.4 promise的状态 - 2.1 Promise States"></a>2.2.4 promise的状态 - 2.1 Promise States</h3><p><img src="ABFB1DBD9BB5DBAF203D0A18339370C5.jpg" alt="IMAGE"><br>图2.2 Promise States</p>
<p>这里的规范说明promise有<code>PENDING</code>、<code>FULFILLED</code>、<code>REJECT</code>三种状态；<br>那么我们先加入这几个常量，在构造函数中声明这个值：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> PENDING = <span class="string">'pending'</span></span><br><span class="line"><span class="keyword">const</span> FULFILLED = <span class="string">'fulfilled'</span></span><br><span class="line"><span class="keyword">const</span> REJECTED = <span class="string">'rejected'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Promise</span>(<span class="params">f</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.state = PENDING;</span><br><span class="line">  <span class="keyword">this</span>.result = <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>然后从2.1.1 和 2.1.2.* 可以知道第一种可以转变为后面两种，后面两种不可再次 <code>transition</code>，我们就再加入一条transition函数让这个函数进行状态变化，并且保存状态变化后的值 <code>value</code>。就如之前提到的，如果<code>state</code>为<code>fulfilled</code>时，<code>result</code>就被看作<code>value</code>，如果<code>state</code>被<code>rejected</code>时，<code>result</code>就被看作<code>reason</code>。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">transition</span> (<span class="params">promise, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (promise.state !== PENDING) <span class="keyword">return</span></span><br><span class="line">  promise.state = state</span><br><span class="line">  promise.result = result</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>此外规范中补充，这里的<code>must not change</code>不意味着不可变对象，而仅仅是内存指向不变。（即这个value值如果是个对象，修改一下对象里的属性并不会破坏这条规则）</p>
<h3 id="2-2-5-then-方法-2-2-The-then-Method"><a href="#2-2-5-then-方法-2-2-The-then-Method" class="headerlink" title="2.2.5 then 方法 - 2.2 The then Method"></a>2.2.5 then 方法 - 2.2 The then Method</h3><p><img src="FCCEDE45412B545C613A24AD01F12664.jpg" alt="IMAGE"><br>图2.3 then大致描述<br>then方法的描述就比较地长了，我们逐一来看：<br>首先规范说要有一个这样的方法，它接收俩参数。既然它这么大方地给了我们就直接复制到之前的代码中吧:D</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.prototype.then = <span class="function"><span class="keyword">function</span>(<span class="params">onFulfilled, onRejected</span>) </span>&#123;</span><br><span class="line">  <span class="comment">//</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后规范的2.2.1 ～ 2.2.3说明了一下这2个参数如果是函数会怎么样、不是函数又会怎么样，我们先略过不看，需要用到的时候我们再提。</p>
<p>2.2.4中提及，这俩参数如果被<code>called</code>。哦那他们首先就是函数了。它们必须等到执行上下文只含平台代码的时候才能被执行。这里是什么意思呢？我们看到它有一个备注<code>3.1</code>，我们看看图2.4。</p>
<p><img src="3C8D9EF3C70C59B930AD8423AA2D0EBA.jpg" alt="IMAGE"><br>图2.4 备注3.1</p>
<p>再补充一份翻译好的规范</p>
<blockquote>
<p>这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行，且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用“宏任务（macro-task）”机制或者“微任务（micro-task）”机制来实现。由于 promise 的实施代码本身就是平台代码（译者注：即都是 JavaScript），故代码自身在处理在处理程序时可能已经包含一个任务调度队列。</p>
</blockquote>
<p>大体意思呢就是这个then方法里的俩兄弟需要等下一个事件队列（macro task/micro task）才能执行。那我们先随便上一个：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.prototype.then = <span class="function"><span class="keyword">function</span>(<span class="params">onFulfilled, onRejected</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 2.2.5 别问，先放着</span></span><br><span class="line">  setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;&#125;, <span class="number">0</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>规范2.2.6说then方法可能会被同一个promise多次调用，如果被调用的话需要按照注册的时候的顺序依次调用。</p>
<p>这下我们知道，他们应该放在一个队列中；其次，它们是不是像我们第一节说的回调一样？一个一个被注册到队列中等到需要的时候再拿出来？</p>
<p>于是我们先去构造函数中注册一个队列保存这些注册的<code>then</code>回调。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Promise</span>(<span class="params">f</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.state = PENDING;</span><br><span class="line">  <span class="keyword">this</span>.result = <span class="literal">null</span>;</span><br><span class="line">  <span class="comment">// 2.2.6</span></span><br><span class="line">  <span class="keyword">this</span>.callbacks = []</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>最后来到2.2.7，先看规范：<br><img src="8FB32EF8C0DA6B0B82E68F63F1B0844B.jpg" alt="IMAGE"><br>图2.5 then方法 条目2.2.7</p>
<p>这里说道：<code>then</code>方法必须返回一个promise，称它为promies2，之前的promise就称它promise1。其实这里的promise1、promise2不是同一个promise对象了，在之后我们会提到关于这个的反模式写法。我们先加入这一条：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.prototype.then = <span class="function"><span class="keyword">function</span>(<span class="params">onFulfilled, onRejected</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// promise2 2.2.7</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="comment">// 2.2.5 别问，先放着</span></span><br><span class="line">  setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;&#125;, <span class="number">0</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后我们发现，2.2.7下面提及的东西都是在执行、返回结果甚至已经要回家过年了#滑稽#（在决议promise1的结果了），我一看这哪行，<code>PENDING</code> 状态要做什么都还没干呢。</p>
<p>这时我们前面提及的，我们要把<code>then</code>以回调的形式注册到队列中（2.2.6）就需要在这里重拳出击，保存目前所给的所有东西（方便恢复案发现场），排队进入队列。同时，这么多逻辑我们也不能都在一个地方写，为了好看一些，我们抽一个方法<code>handleCallback</code>来进行剩余的操作。</p>
<p>为了减少篇幅（文字工作者不利的地方），这里我就把后面要用到的状态(state)和结果(value)都放函数里了。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.prototype.then = <span class="function"><span class="keyword">function</span>(<span class="params">onFulfilled, onRejected</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// promise2 2.2.7</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> callback = &#123; onFulfilled, onRejected, resolve, reject &#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>.state === PENDING) &#123;</span><br><span class="line">      <span class="comment">// 2.2.6</span></span><br><span class="line">      <span class="keyword">this</span>.callbacks.push(callback)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// 2.2.4</span></span><br><span class="line">      setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> </span><br><span class="line">        handleCallback(callback, <span class="keyword">this</span>.state, <span class="keyword">this</span>.result), <span class="number">0</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleCallback</span>(<span class="params">callback, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 接下来要大干一场的地方</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后我们来处理这个<code>handleCallback</code>方法：<br>首先，进了这个方法的promies状态不是笑（fulfilled）就是哭（rejected），我们要对这个进行一个判断。<br>另外，扔进来的callback包我们也拆一下方便下一步工作。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleCallback</span>(<span class="params">callback, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> &#123; onFulfilled, onRejected, resolve, reject &#125; = callback</span><br><span class="line">  <span class="keyword">if</span> (state === FULFILLED) &#123;</span><br><span class="line">    </span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// REJECTED</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>接着我们就要用到前面提及但是未用的2.2.1～2.2.3条款并结合2.2.7<br><img src="223EDF02DF00FB49EC748E72BF34DDA6.jpg" alt="IMAGE"><br>图2.6 then方法 2.2.1～2.2.3条款</p>
<p>2.2.7.1 如果我们执行 <code>onFulfilled</code> 和 <code>onRejected</code> 返回的值（这里称为x），它就使用promise的决议过程处理<code>[[Resolve]](promise2, x)</code>这个值。那么首先这俩兄弟它得是个function才能返回结果x。我们这里就进行一个判断，然后决议它。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleCallback</span>(<span class="params">callback, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> &#123; onFulfilled, onRejected, resolve, reject &#125; = callback</span><br><span class="line">  <span class="keyword">if</span> (state === FULFILLED) &#123;</span><br><span class="line">    <span class="comment">// 进行判断</span></span><br><span class="line">    <span class="keyword">if</span> (isFunction(onFulfilled)) &#123;</span><br><span class="line">      <span class="keyword">const</span> x = onFulfilled(result)</span><br><span class="line">      <span class="comment">// 决议它</span></span><br><span class="line">      resolve(x)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 进行判断</span></span><br><span class="line">    <span class="keyword">if</span> (isFunction(onRejected)) &#123;</span><br><span class="line">      <span class="keyword">const</span> x = onRejected(result)</span><br><span class="line">      <span class="comment">// 决议它</span></span><br><span class="line">      resolve(x)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后看到2.2.7.2 如果执行报错，我们要保护它，扔出一个错误作为promise2决议失败的理由。并且，2.2.7.3-4提出，如果俩兄弟不是函数的时候，跟着promise1的情况决议值</p>
<p>经过文字工作者的简单处理，代码就变成了这亚子：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleCallback</span>(<span class="params">callback, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> &#123; onFulfilled, onRejected, resolve, reject &#125; = callback</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (state === FULFILLED) &#123;</span><br><span class="line">      isFunction(onFulfilled)</span><br><span class="line">      ? resolve(onFulfilled(result))</span><br><span class="line">      <span class="comment">// 2.2.7.3</span></span><br><span class="line">      : resolve(result)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      isFunction(onRejected)</span><br><span class="line">      ? resolve(onRejected(result))</span><br><span class="line">      <span class="comment">// 2.2.7.4</span></span><br><span class="line">      : reject(result)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    <span class="comment">// 2.2.7.2</span></span><br><span class="line">    reject(e)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>至此我们的代码应该是这样的:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> isFunction = <span class="function"><span class="params">obj</span> =&gt;</span> <span class="keyword">typeof</span> obj === <span class="string">'function'</span></span><br><span class="line"><span class="keyword">const</span> toString = <span class="built_in">Object</span>.prototype.toString</span><br><span class="line"><span class="keyword">const</span> isObject = <span class="function"><span class="params">obj</span> =&gt;</span> toString.call(obj) === <span class="string">'[object Object]'</span></span><br><span class="line"><span class="keyword">const</span> isThenable = <span class="function"><span class="params">obj</span> =&gt;</span> (isObject(obj) || isFunction(obj)) &amp;&amp; <span class="string">'then'</span> <span class="keyword">in</span> obj</span><br><span class="line"><span class="keyword">const</span> isPromise = <span class="function"><span class="params">promise</span> =&gt;</span> promise <span class="keyword">instanceof</span> <span class="built_in">Promise</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> PENDING = <span class="string">'pending'</span></span><br><span class="line"><span class="keyword">const</span> FULFILLED = <span class="string">'fulfilled'</span></span><br><span class="line"><span class="keyword">const</span> REJECTED = <span class="string">'rejected'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Promise</span>(<span class="params">f</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.state = PENDING;</span><br><span class="line">  <span class="keyword">this</span>.result = <span class="literal">null</span>;</span><br><span class="line">  <span class="keyword">this</span>.callbacks = []</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">transition</span> (<span class="params">promise, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (promise.state !== PENDING) <span class="keyword">return</span></span><br><span class="line">  promise.state = state</span><br><span class="line">  promise.result = result</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Promise</span>.prototype.then = <span class="function"><span class="keyword">function</span>(<span class="params">onFulfilled, onRejected</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// promise2</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> callback = &#123; onFulfilled, onRejected, resolve, reject &#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>.state === PENDING) &#123;</span><br><span class="line">      <span class="keyword">this</span>.callbacks.push(callback)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> </span><br><span class="line">        handleCallback(callback, <span class="keyword">this</span>.state, <span class="keyword">this</span>.result), <span class="number">0</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleCallback</span>(<span class="params">callback, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> &#123; onFulfilled, onRejected, resolve, reject &#125; = callback</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (state === FULFILLED) &#123;</span><br><span class="line">      isFunction(onFulfilled) ? resolve(onFulfilled(result)) : resolve(result)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      isFunction(onRejected) ? resolve(onRejected(result)) : reject(result)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    reject(e)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>至此我们已经完成了66.66%的工作，因为总共就3条目：D</p>
<h3 id="2-2-6-then-方法-2-3-The-Promise-Resolution-Procedure"><a href="#2-2-6-then-方法-2-3-The-Promise-Resolution-Procedure" class="headerlink" title="2.2.6 then 方法 - 2.3 The Promise Resolution Procedure"></a>2.2.6 then 方法 - 2.3 The Promise Resolution Procedure</h3><p>对于大段的英文，我还是先放中文翻译吧：D</p>
<blockquote>
<p>Promise 解决过程是一个抽象的操作，其需输入一个 promise 和一个值，我们表示为 <a href="promise, x">[Resolve]</a>，如果 x 有 then 方法且看上去像一个 Promise ，解决程序即尝试使 promise 接受 x 的状态；否则其用 x 的值来执行 promise 。</p>
</blockquote>
<blockquote>
<p>这种 thenable 的特性使得 Promise 的实现更具有通用性：只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可；这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。</p>
</blockquote>
<p>直接看中文的好处是我们更熟悉它（大雾<br>需要抠细节的时候再读原文不失为一种折衷的做法。<br>对于这第三部分规范，它大题的意思就是我们promise可以resolve不同类型的值，对于不同的值我们要做不同的处理。</p>
<p>首先它说明了这个方法带着promise和一个参数x。并且2.3.1做了一个基本的判断。话不多说我们开干：<br><img src="C7C592FBB53E9C43F6FE6032772807D9.jpg" alt="IMAGE"><br>图2.7 决议过程1<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolvePromise</span>(<span class="params">promise, x</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(promise === x) <span class="keyword">return</span> reject(<span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'2.3.1'</span>))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>2.3.2：如果x是个promise，那就沿用x的状态，让promise跟着x的状态走。<br>这里就类似继承一般，我们只需要在x上注册一个then，把promise的决议方法放进去即可完成要求。<br><img src="03DA27A461C8807C32A589D19C1193E9.jpg" alt="IMAGE"><br>图2.8 决议过程2</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolvePromise</span>(<span class="params">promise, x</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(promise === x) <span class="keyword">return</span> reject(<span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'2.3.1'</span>))</span><br><span class="line">  <span class="keyword">if</span>(isPromise(x)) <span class="keyword">return</span> x.then(resolve, reject)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>来到2.3.3，发现这一条特别长。一条一条解读：<br>如果我们发现x是一个<code>thenable</code>对象或者函数<br>1: 设一个变量 <code>then</code> 指向 x.then<br>2: 如果取 <code>x</code> 的 <code>then</code> 属性失败了，捕捉错误 <code>e</code> 并决议作为 <code>reason</code> 决议 promise 失败<br>3: 如果 <code>then</code> 是一个 <code>function</code> 就使它的this指向为x，接收俩个参数分别称为 <code>resolvePromise</code> 和 <code>rejectPromise</code></p>
<p>考虑一下，什么方法可以控制一个函数被调用时的 <code>this</code> 指向并且返回的还是函数。</p>
<p>2.3.3.3.1 和 2.3.3.3.2 这两条与 2.3.2.2 和 2.3.2.3 有些类似，我们只需要注册一个 <code>then</code> 方法，将promise的控制权注册进去即可。</p>
<p>剩余的2.3.3.3 ～ 2.3.3.4 先保留，我们之后来讲。</p>
<p><img src="6EC23FAAFDF54CAFD9AA7D9A20620637.jpg" alt="IMAGE"><br>图2.9 决议过程3</p>
<p>分别判断 <code>x</code> 是否为 <code>promise</code> 自身、 新的promise、 <code>thenable</code>对象之后，剩余的处理方法即2.3.4，以x的决议promise成功</p>
<p>代码如下：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolvePromise</span>(<span class="params">promise, x, resolve, reject</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(promise === x) <span class="keyword">return</span> reject(<span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'2.3.1'</span>))</span><br><span class="line">  <span class="keyword">if</span>(isPromise(x)) <span class="keyword">return</span> x.then(resolve, reject)</span><br><span class="line">  <span class="keyword">if</span>(isThenable(x)) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; then &#125; = x</span><br><span class="line">      <span class="keyword">if</span>(isFunction(then)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(then.bind(x)).then(resolve, reject)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">      reject(e)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  resolve(x)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>到这里我们完成了规范主要的部分，剩下的工作就是要拼接+完成之前跳过的细节条目。</p>
<p>首先，为了应对测试套件，我们需要写俩个静态方法：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.resolve = <span class="function"><span class="params">value</span> =&gt;</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve</span>) =&gt;</span> resolve(value))</span><br><span class="line"><span class="built_in">Promise</span>.reject = <span class="function"><span class="params">reason</span> =&gt;</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">_, reject</span>) =&gt;</span> reject(reason))</span><br></pre></td></tr></table></figure></p>
<p>我们发现这时候 <code>transition</code> 方法和 <code>resolvePromise</code> 方法还没被使用到。再思考一下平时我们使用promise的方式：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;&#125;)</span><br></pre></td></tr></table></figure></p>
<p>于是我们需要在promise的构造函数中做以下工作：</p>
<ol>
<li>使用 <code>transition</code> 对单个promise进行状态转移</li>
<li>使用 <code>handleCallback</code> 对当前的promise转移到下一个promise状态</li>
<li>使用 <code>resolvePromise</code> 来处理promise决议成功后的result值</li>
<li>当 <code>resolve</code> 或者 <code>reject</code> 被多次调用时，只取第一次调用的结果，忽略后面的调用。（2.3.3.3.3）</li>
<li>将 <code>resolve</code> 和 <code>reject</code> 作为参数，传入 <code>f</code> 函数，后续在<code>new Promise</code>时就把 <code>f</code> 供出去让使用者可以使用 <code>resovle</code> 和 <code>reject</code> 方法。</li>
<li>构建 <code>onFulfilled</code> 和 <code>onRejected</code> 提供将当前promise状态转换的能力</li>
</ol>
<p>这里直接贴上promies构造函数的代码，并且在代码中注释以上完成的功能点：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Promise</span>(<span class="params">f</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.state = PENDING;</span><br><span class="line">  <span class="keyword">this</span>.result = <span class="literal">null</span>;</span><br><span class="line">  <span class="keyword">this</span>.callbacks = []</span><br><span class="line">  <span class="comment">// 6</span></span><br><span class="line">  <span class="keyword">let</span> onFulfilled = <span class="function"><span class="params">value</span> =&gt;</span> transition(<span class="keyword">this</span>, FULFILLED, value) <span class="comment">// 1</span></span><br><span class="line">  <span class="keyword">let</span> onRejected = <span class="function"><span class="params">reason</span> =&gt;</span> transition(<span class="keyword">this</span>, REJECTED, reason) <span class="comment">// 1</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> flag = <span class="literal">false</span>; <span class="comment">// 4</span></span><br><span class="line">  <span class="keyword">const</span> resolve = <span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(flag) <span class="keyword">return</span> <span class="comment">// 4</span></span><br><span class="line">    flag = <span class="literal">true</span> <span class="comment">// 4</span></span><br><span class="line">    resolvePromise(<span class="keyword">this</span>, value, onFulfilled, onRejected) <span class="comment">// 3</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> reject = <span class="function"><span class="params">reason</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(flag) <span class="keyword">return</span> <span class="comment">// 4</span></span><br><span class="line">    flag = <span class="literal">true</span> <span class="comment">// 4</span></span><br><span class="line">    onRejected(reason)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="comment">// 5</span></span><br><span class="line">    f(resolve, reject)</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    reject(e)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>这里的 <code>try...catch</code> 是因为2.2.7.2中，注册的callback如果在 <code>transition</code> 到新的状态的过程中抛错，可以在最外层被捕获到，直接决议promies失败。</p>
<p>然后我们就来测试一下现在的代码<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run <span class="built_in">test</span></span><br></pre></td></tr></table></figure></p>
<p><img src="AAB4878614D10C4DFE9A05B531E05484.jpg" alt="IMAGE"><br>图2.10 第一次测试结果</p>
<p>这里我看到第一个错误结果就停止继续跑下去了。我们看一下这里报错对应的说明：</p>
<blockquote>
<p>applied to a promise rejected and then chained off of</p>
</blockquote>
<p>这里我们大概能猜出<strong>链式</strong>这一问题，回忆之前的代码，我们似乎没有处理注册在 <code>callbacks</code> 队列里的 <code>then</code> 回调，是否是这个原因导致呢？</p>
<p>我们再实现一个 <code>handleCallbacks</code> 来处理队列里所有的 <code>then</code> 回调，这里注意我们还是需要遵守2.2.4使其与当前执行上下文不在一起。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">transition</span> (<span class="params">promise, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (promise.state !== PENDING) <span class="keyword">return</span></span><br><span class="line">  promise.state = state</span><br><span class="line">  promise.result = result</span><br><span class="line"></span><br><span class="line">  setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> handleCallbacks(promise.callbacks, state, result), <span class="number">0</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleCallbacks</span>(<span class="params">callbacks, state, result</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(callbacks.length) handleCallback(callbacks.shift(), state, result) </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>完整代码见：<a href="https://github.com/hemisu/promise-aplus-impl" target="_blank" rel="noopener">repo</a></p>
<p>我们最后跑一下测试。</p>
<p><img src="CE07C5A06BE97BA8F62F00604CA42898.jpg" alt="IMAGE"><br>图2.11 第二次测试结果</p>
<p>可以看到我们已经实现了一个Promies/A+规范（鼓掌）</p>
<p>这里插一个小tip，我们如果把macroTask替换成microTask会不会更快一些捏？这里使用 <code>process.nextTick</code> 来替换 <code>setTimeout</code></p>
<p><img src="182425CE19C05E16A5AD2D5631AE2FEA.jpg" alt="IMAGE"><br>图2.12 第三次测试结果</p>
<p>可以看到跑完测试的时间减少了3s，非常大的进步：D</p>
<h2 id="2-3-增强为ES6的promise"><a href="#2-3-增强为ES6的promise" class="headerlink" title="2.3 增强为ES6的promise"></a>2.3 增强为ES6的promise</h2><p>ES6需要补充的方法如下，可以看下面的一个是规范地址，一个是mdn地址；除去还在草案中的allsettled等方法，前面我们已经实现了打勾的部分，我们还需要实现剩下的catch、all、race。</p>
<ol>
<li><a href="https://tc39.es/ecma262/#sec-promise-objects" target="_blank" rel="noopener">https://tc39.es/ecma262/#sec-promise-objects</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally" target="_blank" rel="noopener">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally</a></li>
</ol>
<p><img src="4C2A96F3E8BA48D1C40BE67F84ED4D3E.jpg" alt="IMAGE"><br>图2.13 需要完成的方法</p>
<h3 id="2-3-1-Promise-prototype-catch"><a href="#2-3-1-Promise-prototype-catch" class="headerlink" title="2.3.1 Promise.prototype.catch"></a>2.3.1 Promise.prototype.catch</h3><p>这个方法其实就是一个语法糖，实现起来十分方便<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.prototype.catch = <span class="function">(<span class="params">f</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">this</span>.then(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    reject(reason)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="2-3-2-Promise-all"><a href="#2-3-2-Promise-all" class="headerlink" title="2.3.2 Promise#all"></a>2.3.2 Promise#all</h3><p>实现 <code>promise.all</code> 方法主要注意以下几点：</p>
<ol>
<li>它不同于还在草案的<code>allSettled</code>，<code>all</code> 方法在决议失败时只需要返回第一个失败的错误。</li>
<li>传入 <code>all</code> 方法的promises是一个数组，全部决议成功后返回的也是一个结果数组，且顺序与promises一致。</li>
</ol>
<p>接下来的工作我们就只需要判断每个值是否为promise，然后注册在 <code>then</code> 中进行处理。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.all = <span class="function">(<span class="params">promises = []</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> results = [];</span><br><span class="line">  <span class="keyword">let</span> count = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">const</span> fn = <span class="function">(<span class="params">i, resolve</span>) =&gt;</span> <span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">    count++</span><br><span class="line">    results[i] = value</span><br><span class="line">    <span class="keyword">if</span>(count === promises.length) resolve(results)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    promises.forEach(<span class="function">(<span class="params">promise, i</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(isPromise(promise)) &#123;</span><br><span class="line">          promise.then(fn(i, resolve), reject)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">        reject(e)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="2-3-2-Promise-race"><a href="#2-3-2-Promise-race" class="headerlink" title="2.3.2 Promise#race"></a>2.3.2 Promise#race</h3><p>其实实现了 <code>all</code> 方法后，<code>race</code> 的方法的实现就水到渠成了。我们把上面的<code>fn</code>拿走就可以完成了</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.race = <span class="function">(<span class="params">promises = []</span>) =&gt;</span> &#123;  </span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    promises.forEach(<span class="function">(<span class="params">promise</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(isPromise(promise)) &#123;</span><br><span class="line">          promise.then(resolve, reject)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">        reject(e)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>最后跑一下测试，<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.all([</span><br><span class="line">  delay(<span class="number">1</span>),</span><br><span class="line">  delay(<span class="number">2</span>),</span><br><span class="line">  delay(<span class="number">4</span>),</span><br><span class="line">]).then(<span class="function"><span class="params">results</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'results'</span>, results);</span><br><span class="line">  <span class="built_in">console</span>.timeEnd(TIME_LOG_NAME)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">Promise</span>.race([</span><br><span class="line">  delay(<span class="number">1</span>),</span><br><span class="line">  delay(<span class="number">2</span>),</span><br><span class="line">  delay(<span class="number">4</span>),</span><br><span class="line">]).then(<span class="function"><span class="params">results</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'results'</span>, results);</span><br><span class="line">  <span class="built_in">console</span>.timeEnd(TIME_LOG_NAME)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p>
<p>最后一个 <code>finally</code> 方法交给大家来解决吧～欢迎在下面提出</p>
<h1 id="3-总结"><a href="#3-总结" class="headerlink" title="3 总结"></a>3 总结</h1><h2 id="3-1实现promise之后的思考"><a href="#3-1实现promise之后的思考" class="headerlink" title="3.1实现promise之后的思考"></a>3.1实现promise之后的思考</h2><h3 id="3-1-1-它是一个callback-management"><a href="#3-1-1-它是一个callback-management" class="headerlink" title="3.1.1 它是一个callback management"></a>3.1.1 它是一个callback management</h3><p>正如前面所说的，我们可以把promise看作是一个callback management：<br>也正如它内置状态所展示的一样，它可以处理0～1个异步结果。<br>如果有了解RxJS的同学，我们可以RxJS当作是一个可以处理0～Infinite个异步结果的库。<br>这无疑是一种能力的增强，同时也是对编码人员的挑战。如果实际的场景没有这么大的需求，我们不会轻易上这类工具。</p>
<p><img src="A4CA555A1D97807117682BB7B8B2233A.jpg" alt="IMAGE"></p>
<p>图3.1 Callback Management</p>
<h3 id="3-1-2-能手写promies实现，就一定精通promies？"><a href="#3-1-2-能手写promies实现，就一定精通promies？" class="headerlink" title="3.1.2 能手写promies实现，就一定精通promies？"></a>3.1.2 能手写promies实现，就一定精通promies？</h3><p>我们刚刚实现的Promise/A+ 规范，重点围绕的是 How to implement 通过实现规范，或者所谓的吃透底层源码，以期打通任督二脉的武学幻想，是不切实际的。 想要精通 promises，还得在日常开发的各个异步场景中，多加思考和训练。 我们应当把精通 promises 定义为：<br><strong>善于在各种异步场景中使用 promises 解决问题。</strong><br>这里就很推荐promise迷你书 <sup><a href="http://liubin.org/promises-book/" title="promise迷你书" target="_blank" rel="noopener">3</a></sup></p>
<h3 id="3-1-3-Promise-是比-callback-更先进的异步方案？"><a href="#3-1-3-Promise-是比-callback-更先进的异步方案？" class="headerlink" title="3.1.3 Promise 是比 callback 更先进的异步方案？"></a>3.1.3 Promise 是比 callback 更先进的异步方案？</h3><p>正如前面所说的等价交换，语义化和标准化，不意味着能力的增强，它也有可能导致能力的减弱；所以谈不上先进，因为减少心智负担也是重要的一个环节。</p>
<ul>
<li>减少编码的心智负担，提高代码可维护性</li>
<li>只有两种callback路径，削弱了灵活性</li>
</ul>
<p>但是我们也要发现的是：promise 的 <code>then</code> 只支持 <code>onFulfilled</code> 和 <code>onRejected</code> 两种 callback 路径，属于对所有可能的 callback 路径的简化。<br>同样是给有兴趣的同学，<a href="https://github.com/callbag/callbag" title="callbag" target="_blank" rel="noopener">callbag</a>库可以使用多个callback函数组合，实现rxjs的observables和iterables。</p>
<h2 id="3-2-反模式"><a href="#3-2-反模式" class="headerlink" title="3.2 反模式"></a>3.2 反模式</h2><p>最后的最后，我们来看几个反模式。</p>
<h3 id="3-2-1-Anti-patterns-1"><a href="#3-2-1-Anti-patterns-1" class="headerlink" title="3.2.1 Anti-patterns 1"></a>3.2.1 Anti-patterns 1</h3><p><img src="A8DF6F5D79C0E2EB9B9C138C846E4B2A.jpg" alt="IMAGE"><br>图3.1 Anti-patterns 1</p>
<p>这块的问题在于没有使用promise方法链。</p>
<ol>
<li><code>promise.then</code>中产生的异常不会被外部捕获 </li>
<li>每次 <code>promise.then</code> 调用都会返回一个新创建的promise对象，不能得到 <code>then</code> 的返回值</li>
</ol>
<p>解决方法如图3.2所示<br><img src="305E536E9AE1C3D8BC74C284C27D441D.jpg" alt="IMAGE"><br>图3.2 Anti-patterns 1 resolution</p>
<h3 id="3-2-2-Anti-patterns-2"><a href="#3-2-2-Anti-patterns-2" class="headerlink" title="3.2.2 Anti-patterns 2"></a>3.2.2 Anti-patterns 2</h3><p><img src="25393614A3D21E15B2D75EB6CC667F46.jpg" alt="IMAGE"><br>图3.3 Anti-patterns 2</p>
<p>这个不是一个很严重的反模式，不过还记不记得我们前面实现的时候有写一个resolvePromise方法可以处理不同情况下的result。利用好这一点：D</p>
<p><img src="1F5F81C12726EC5BCDA78E15E54A7E35.jpg" alt="IMAGE"><br>图3.4 Anti-patterns 2 resolution</p>
<h3 id="3-2-3-Anti-patterns-3"><a href="#3-2-3-Anti-patterns-3" class="headerlink" title="3.2.3 Anti-patterns 3"></a>3.2.3 Anti-patterns 3</h3><p><img src="FE03CF8560B940DA8051BF0D8F8F2AE1.jpg" alt="IMAGE"><br>图3.5 Anti-patterns 3</p>
<p>这里的问题就在于，<code>somethingElseAsync</code>中抛出的函数不会被<code>handleMyError</code>处理。<br>我们可以再注册一个 <code>then</code> 方法；或者直接使用 <code>catch</code> 语法糖。</p>
<p><img src="786E3BB5C3937897950BB2EB47F5C120.jpg" alt="IMAGE"><br>图3.4 Anti-patterns 3 resolution</p>
<h2 id="3-3-附录"><a href="#3-3-附录" class="headerlink" title="3.3 附录"></a>3.3 附录</h2><p>参考：</p>
<ul>
<li><p>100行代码实现promise<br><a href="https://mp.weixin.qq.com/s/qdJ0Xd8zTgtetFdlJL3P1g" target="_blank" rel="noopener">https://mp.weixin.qq.com/s/qdJ0Xd8zTgtetFdlJL3P1g</a></p>
</li>
<li><p>深入理解Javascript异步 part1 part2<br><a href="https://github.com/wangfupeng1988/js-async-tutorial" target="_blank" rel="noopener">https://github.com/wangfupeng1988/js-async-tutorial</a></p>
</li>
<li><p>promies迷你书<br><a href="http://liubin.org/promises-book/" target="_blank" rel="noopener">http://liubin.org/promises-book/</a></p>
</li>
<li><p>聊一聊promise的前世今生<br><a href="https://www.cnblogs.com/tarol/p/9042407.html" target="_blank" rel="noopener">https://www.cnblogs.com/tarol/p/9042407.html</a></p>
</li>
<li><p>更多的反模式<br><a href="http://taoofcode.net/promise-anti-patterns/" target="_blank" rel="noopener">http://taoofcode.net/promise-anti-patterns/</a></p>
</li>
</ul>
<h2 id="答案1-事件绑定算不算异步操作"><a href="#答案1-事件绑定算不算异步操作" class="headerlink" title="答案1 事件绑定算不算异步操作"></a>答案1 事件绑定算不算异步操作</h2><p><span id="17:31:42-1"><a href="#17:31:42">⬆️ 返回 ⬆️</a></span></p>
<p>这个问题是在我研究这个主题的时候看到的一个讨论：<br><a href="https://github.com/wangfupeng1988/js-async-tutorial/blob/master/part1-basic/03-event-bind.md" target="_blank" rel="noopener">refer: 事件绑定算不算异步？</a></p>
<p>个人的见解在于，事件绑定是否属于异步操作需要看触发源是否在同一个 <a href="https://es5.github.io/#x10.3" target="_blank" rel="noopener">execution context</a> 中。</p>
<p>通过判断有没有runtime的介入：如果仅仅是在js engine下，它只负责取消息，不负责生产消息；那么在代码中编写的事件绑定就属于一类同步操作；</p>
<p>如果有runtime的介入，比如浏览器DOM事件发送一条合成事件（SyntheticEvent）统一处理，或者是监听onload消息等，js engine在清理完call-stack后接受接收到这条消息并且执行，这时候就可以认为它是异步操作。</p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/javascript/" rel="tag"># javascript</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2018/10/17/2018-10-17-React-Components-Elements-and-Instance/" rel="next" title="一次打包错误 引起的对 React Components, Elements, Instance三者的认识">
                <i class="fa fa-chevron-left"></i> 一次打包错误 引起的对 React Components, Elements, Instance三者的认识
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2020/06/20/promise-pool/" rel="prev" title="js异步初探 - 图解并发控制">
                js异步初探 - 图解并发控制 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image" src="/images/avatar.jpg" alt="何米酥">
            
              <p class="site-author-name" itemprop="name">何米酥</p>
              <p class="site-description motion-element" itemprop="description">Just do...</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">202</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">8</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">78</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/hemisu" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://www.zhihu.com/people/hemisu" target="_blank" title="知乎">
                      
                        <i class="fa fa-fw fa-globe"></i>知乎</a>
                  </span>
                
            </div>
          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#1-前言"><span class="nav-number">1.</span> <span class="nav-text">1. 前言</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-1-为何会有异步"><span class="nav-number">1.1.</span> <span class="nav-text">1.1 为何会有异步</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#1-2-异步的实现原理"><span class="nav-number">1.2.</span> <span class="nav-text">1.2 异步的实现原理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-1-常见的异步操作："><span class="nav-number">1.2.1.</span> <span class="nav-text">1.2.1 常见的异步操作：</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-2-事件绑定算不算异步操作？"><span class="nav-number">1.2.2.</span> <span class="nav-text">1.2.2 事件绑定算不算异步操作？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#1-3-异步的演进方案"><span class="nav-number">1.3.</span> <span class="nav-text">1.3 异步的演进方案</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-1-deferred和-promise有什么区别？"><span class="nav-number">1.3.1.</span> <span class="nav-text">1.3.1 $.deferred和$.promise有什么区别？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-2-callback的引入"><span class="nav-number">1.3.2.</span> <span class="nav-text">1.3.2 callback的引入</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-3-promise也是callback？"><span class="nav-number">1.3.3.</span> <span class="nav-text">1.3.3 promise也是callback？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-4-generator也是callback？"><span class="nav-number">1.3.4.</span> <span class="nav-text">1.3.4 generator也是callback？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-5-最后演进到async-await"><span class="nav-number">1.3.5.</span> <span class="nav-text">1.3.5 最后演进到async/await</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#2-Promise"><span class="nav-number">2.</span> <span class="nav-text">2 Promise</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#2-1-概览"><span class="nav-number">2.1.</span> <span class="nav-text">2.1 概览</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-1-图2-1中俩者的promise有啥区别？"><span class="nav-number">2.1.1.</span> <span class="nav-text">2.1.1 图2.1中俩者的promise有啥区别？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-2-实现一个-Promise-A-规范"><span class="nav-number">2.2.</span> <span class="nav-text">2.2 实现一个 Promise/A+ 规范</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-1-前期工作"><span class="nav-number">2.2.1.</span> <span class="nav-text">2.2.1 前期工作</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-2-开始实现"><span class="nav-number">2.2.2.</span> <span class="nav-text">2.2.2 开始实现</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-3-术语-1-terminology"><span class="nav-number">2.2.3.</span> <span class="nav-text">2.2.3 术语 - 1.terminology</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-4-promise的状态-2-1-Promise-States"><span class="nav-number">2.2.4.</span> <span class="nav-text">2.2.4 promise的状态 - 2.1 Promise States</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-5-then-方法-2-2-The-then-Method"><span class="nav-number">2.2.5.</span> <span class="nav-text">2.2.5 then 方法 - 2.2 The then Method</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-6-then-方法-2-3-The-Promise-Resolution-Procedure"><span class="nav-number">2.2.6.</span> <span class="nav-text">2.2.6 then 方法 - 2.3 The Promise Resolution Procedure</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-3-增强为ES6的promise"><span class="nav-number">2.3.</span> <span class="nav-text">2.3 增强为ES6的promise</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-1-Promise-prototype-catch"><span class="nav-number">2.3.1.</span> <span class="nav-text">2.3.1 Promise.prototype.catch</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-2-Promise-all"><span class="nav-number">2.3.2.</span> <span class="nav-text">2.3.2 Promise#all</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-2-Promise-race"><span class="nav-number">2.3.3.</span> <span class="nav-text">2.3.2 Promise#race</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#3-总结"><span class="nav-number">3.</span> <span class="nav-text">3 总结</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#3-1实现promise之后的思考"><span class="nav-number">3.1.</span> <span class="nav-text">3.1实现promise之后的思考</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-1-它是一个callback-management"><span class="nav-number">3.1.1.</span> <span class="nav-text">3.1.1 它是一个callback management</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-2-能手写promies实现，就一定精通promies？"><span class="nav-number">3.1.2.</span> <span class="nav-text">3.1.2 能手写promies实现，就一定精通promies？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-3-Promise-是比-callback-更先进的异步方案？"><span class="nav-number">3.1.3.</span> <span class="nav-text">3.1.3 Promise 是比 callback 更先进的异步方案？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-2-反模式"><span class="nav-number">3.2.</span> <span class="nav-text">3.2 反模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-1-Anti-patterns-1"><span class="nav-number">3.2.1.</span> <span class="nav-text">3.2.1 Anti-patterns 1</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-2-Anti-patterns-2"><span class="nav-number">3.2.2.</span> <span class="nav-text">3.2.2 Anti-patterns 2</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-3-Anti-patterns-3"><span class="nav-number">3.2.3.</span> <span class="nav-text">3.2.3 Anti-patterns 3</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-3-附录"><span class="nav-number">3.3.</span> <span class="nav-text">3.3 附录</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#答案1-事件绑定算不算异步操作"><span class="nav-number">3.4.</span> <span class="nav-text">答案1 事件绑定算不算异步操作</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      
        <div class="back-to-top">
          <i class="fa fa-arrow-up"></i>
          
            <span id="scrollpercent"><span>0</span>%</span>
          
        </div>
      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; 2015 &mdash; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">何米酥</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>




        







        
      </div>
    </footer>

    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
